package org.dding.core.management;

import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import org.dding.core.manager.IModuleManager;
import org.jboss.modules.LocalLoader;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.modules.ModuleLoadError;
import org.jboss.modules.ModuleLoadException;

public class ModuleContainer implements ModuleContainerMBean {

	private Reference<IModuleManager> reference;
	private static final RuntimePermission MODULE_REDEFINE_ANY_PERM = new RuntimePermission(
			"canRedefineAnyModule");

	public ModuleContainer(final IModuleManager moduleManager) {
		reference = new WeakReference<IModuleManager>(moduleManager);
	}

	public String getDescription() {
		return getModuleManager().getModuleLoader().toString();
	}

	public long getLinkTime() {
		return getModuleManager().getModuleLoader().getLinkTime();
	}

	public long getLoadTime() {
		return getModuleManager().getModuleLoader().getLoadTime();
	}

	public long getClassDefineTime() {
		return getModuleManager().getModuleLoader().getClassLoadTime();
	}

	public int getScanCount() {
		return getModuleManager().getModuleLoader().getScanCount();
	}

	public int getLoadedModuleCount() {
		return getModuleManager().getModuleLoader().getModuleMap().size();
	}

	public int getRaceCount() {
		return getModuleManager().getModuleLoader().getRaceCount();
	}

	public int getClassCount() {
		return getModuleManager().getModuleLoader().getClassCount();
	}

	public List<String> queryLoadedModuleNames() {
		final Set<ModuleIdentifier> identifiers = getModuleManager().getModuleLoader().getModuleMap()
				.keySet();
		final ArrayList<String> list = new ArrayList<String>(identifiers.size());
		for (ModuleIdentifier identifier : identifiers) {
			list.add(identifier.toString());
		}
		Collections.sort(list);
		return list;
	}

	public String dumpAllModuleInformation() {
		final StringBuilder b = new StringBuilder();
		for (String name : queryLoadedModuleNames()) {
			doDumpModuleInformation(name, b);
		}
		return b.toString();
	}

	public String dumpModuleInformation(final String name) {
		final StringBuilder b = new StringBuilder();
		doDumpModuleInformation(name, b);
		return b.toString();
	}

	private void doDumpModuleInformation(final String name,
			final StringBuilder b) {
		ModuleInfo description = getModuleDescription(name);
		b.append("Module ").append(name).append('\n');
		b.append("    Class loader: ").append(description.getClassLoader())
				.append('\n');
		String fallbackLoader = description.getFallbackLoader();
		if (fallbackLoader != null)
			b.append("    Fallback loader: ").append(fallbackLoader)
					.append('\n');
		String mainClass = description.getMainClass();
		if (mainClass != null)
			b.append("    Main Class: ").append(mainClass).append('\n');
		List<ResourceLoaderInfo> loaders = description.getResourceLoaders();
		b.append("    Resource Loaders:\n");
		for (ResourceLoaderInfo loader : loaders) {
			b.append("        Loader Type: ").append(loader.getType())
					.append('\n');
			b.append("        Paths:\n");
			for (String path : loader.getPaths()) {
				b.append("            ").append(path).append('\n');
			}
		}
		b.append("    Dependencies:\n");
		for (DependencyInfo dependencyInfo : description.getDependencies()) {
			b.append("        Type: ")
					.append(dependencyInfo.getDependencyType()).append('\n');
			String moduleName = dependencyInfo.getModuleName();
			if (moduleName != null) {
				b.append("        Module Name: ").append(moduleName)
						.append('\n');
			}
			if (dependencyInfo.isOptional())
				b.append("        (optional)\n");
			b.append("        Export Filter: ")
					.append(dependencyInfo.getExportFilter()).append('\n');
			b.append("        Import Filter: ")
					.append(dependencyInfo.getImportFilter()).append('\n');
			String localLoader = dependencyInfo.getLocalLoader();
			if (localLoader != null) {
				b.append("        Local Loader: ").append(localLoader)
						.append('\n');
				b.append("        Paths:\n");
				for (String path : dependencyInfo.getLocalLoaderPaths()) {
					b.append("            ").append(path).append('\n');
				}
			}
		}
	}

	public boolean unloadModule(final String name) {
		return getModuleManager().unloadModule(name);
	}

	public boolean loadModule(final String name, boolean unzip) {
		final Module module;
		try {
			module = getModuleManager().loadModule(name, unzip);
			if (module == null)
				return false;
		} catch (IllegalArgumentException e) {
			throw e;
		} catch (ModuleLoadException e) {
			throw new IllegalStateException("Module load failure for module "
					+ name + ": " + e.toString());
		} catch (IOException e) {
			throw new IllegalStateException("Module load failure for module "
					+ name + ": " + e.toString());
		}
		return true;
	}

	public void refreshResourceLoaders(final String name) {
		final SecurityManager sm = System.getSecurityManager();
		if (sm != null) {
			sm.checkPermission(MODULE_REDEFINE_ANY_PERM);
		}
		final Module module = getModuleManager().findModule(name);
		getModuleManager().getModuleLoader().refreshResourceLoaders(module);
	}

	public void relink(final String name) {
		final SecurityManager sm = System.getSecurityManager();
		if (sm != null) {
			sm.checkPermission(MODULE_REDEFINE_ANY_PERM);
		}
		final Module module = getModuleManager().findModule(name);
		try {
			getModuleManager().getModuleLoader().relink(module);
		} catch (ModuleLoadException e) {
			throw new IllegalStateException("Module load failure for module "
					+ name + ": " + e.toString());
		}
	}

	public List<DependencyInfo> getDependencies(final String name) {
		return getModuleManager().getDependencies(name);
	}

	public List<ResourceLoaderInfo> getResourceLoaders(final String name) {
		return getModuleManager().getResourceLoaders(name);
	}

	public ModuleInfo getModuleDescription(String name) {
		return getModuleManager().getModuleDescription(name);
	}

	public SortedMap<String, List<String>> getModulePathsInfo(
			final String name, final boolean exports) {
		final Module module = getModuleManager().findModule(name);
		final Map<String, List<LocalLoader>> paths;
		try {
			paths = module.getPathsUnchecked(exports);
		} catch (ModuleLoadError e) {
			throw new IllegalArgumentException("Error loading module " + name
					+ ": " + e.toString());
		}
		final TreeMap<String, List<String>> result = new TreeMap<String, List<String>>();
		for (Map.Entry<String, List<LocalLoader>> entry : paths.entrySet()) {
			final String path = entry.getKey();
			final List<LocalLoader> loaders = entry.getValue();
			if (loaders.isEmpty()) {
				result.put(path, Collections.<String> emptyList());
			} else if (loaders.size() == 1) {
				result.put(path, Collections.<String> singletonList(loaders
						.get(0).toString()));
			} else {
				final ArrayList<String> list = new ArrayList<String>();
				for (LocalLoader localLoader : loaders) {
					list.add(localLoader.toString());
				}
				result.put(path, list);
			}
		}
		return result;
	}

	private IModuleManager getModuleManager() {
		final IModuleManager loader = reference.get();
		if (loader == null) {
			throw new IllegalStateException("Module Manager is gone");
		}
		return loader;
	}
}